package com.yueya.auth.config;
import com.yueya.auth.cache.PmsCacheManager;
import com.yueya.auth.filter.*;
import com.yueya.auth.realm.AccountRealm;
import com.yueya.auth.service.AccountInfoProvider;
import com.yueya.auth.service.AuthTokenService;
import com.yueya.auth.session.PmsSessionDao;
import com.yueya.auth.session.PmsSessionFactory;
import com.yueya.auth.session.PmsSubjectFactory;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableConfigurationProperties(AuthProperties.class)
@Import({LogConfig.class})
@ConditionalOnProperty(
        value = "auth.enable"
)
public class AuthConfig {
    @Autowired
    private AuthProperties properties;
    @Autowired(required = false)
    private AccountInfoProvider provider;
    @Autowired
    private PmsSessionDao sessionDao;
    @Autowired
    private PmsCacheManager cacheManager;
    @Autowired
    private AuthTokenService tokenService;
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        SessionFactory sessionFactory = new PmsSessionFactory();
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        sessionManager.setSessionFactory(sessionFactory);
        sessionManager.setSessionDAO(sessionDao);
        if(provider==null && properties.isServer()){
            throw new NullPointerException("未实现AccountInfoProvider,请在server工程实现该接口");
        }
        if(tokenService==null && properties.isJwtMode()){
            throw new NullPointerException("未实现AuthTokenService,请在server工程实现该接口");
        }
        AccountRealm realm=new AccountRealm();
        realm.setProperties(properties);
        realm.setProvider(provider);
        realm.setMessageConfig(MessageConfig.ins());
        if(properties.isJwtMode()){
            sessionManager.setSessionIdCookieEnabled(false);
            sessionManager.setSessionValidationSchedulerEnabled(false);
            DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
            DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
            evaluator.setSessionStorageEnabled(false);
            subjectDAO.setSessionStorageEvaluator(evaluator);
            securityManager.setSubjectFactory(new PmsSubjectFactory());
            securityManager.setSubjectDAO(subjectDAO);
        } else {
            realm.setCachingEnabled(true);
            realm.setAuthenticationCachingEnabled(true);
        }
        realm.setCacheManager(cacheManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRealm(realm);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilters(SecurityManager manager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        Map<String,String> filters=new HashMap<>();
        filters.put(properties.getAdminPath()+"/logout","logout");
        if (properties.isJwtMode()) {
            filters.put(properties.getAdminPath()+"/**","jwt,forceLogout,user");
        }else {
            filters.put(properties.getAdminPath()+"/**","forceLogout,user");
        }
        filters.put(properties.getFrontPath()+"/**","anon");
        filters.put(properties.getLoginUrl(),"authc");
        shiroFilterFactoryBean.setLoginUrl(properties.getLoginUrl());
        shiroFilterFactoryBean.setSuccessUrl(properties.getSuccessUrl());
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filters);
        shiroFilterFactoryBean.setSecurityManager(manager);
        AccountFilter accountFilter = new AccountFilter();
        ForceLogoutFilter forceLogoutFilter = new ForceLogoutFilter();
        JwtFilter jwtFilter = new JwtFilter();
        PmsUserFilter pmsUserFilter = new PmsUserFilter();
        PmsLogoutFilter logoutFilter = new PmsLogoutFilter();
        forceLogoutFilter.setAuthProperties(properties);
        accountFilter.setAuthProperties(properties);
        accountFilter.setTokenService(tokenService);
        jwtFilter.setAuthProperties(properties);
        pmsUserFilter.setAuthProperties(properties);
        logoutFilter.setAuthProperties(properties);
        shiroFilterFactoryBean.getFilters().put("authc",accountFilter);
        shiroFilterFactoryBean.getFilters().put("jwt",jwtFilter);
        shiroFilterFactoryBean.getFilters().put("user",pmsUserFilter);
        shiroFilterFactoryBean.getFilters().put("forceLogout",forceLogoutFilter);
        shiroFilterFactoryBean.getFilters().put("logout",logoutFilter);
        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;

    }

}
